function [x w v] = lagpts(n,int,meth)
%LAGPTS  Laguerre points and Gauss-Laguerre Quadrature Weights.
%  LAGPTS(N) returns N Laguerre points X in (0,inf).
%
%  [X,W] = LAGPTS(N) returns also a row vector W of weights for Gauss-Laguerre 
%  quadrature. [X,W,V] = LAGPTS(N) returns in addition a column vector V
%  of the barycentric weights corresponding to X.
%
%  LAGPTS(N,D) scales the nodes and weights for the semi-infinite domain D. 
%  D can be either a domain object or a vector with two components.
%
%  [X,W] = LAGPTS(N,METHOD) allows the user to select which method to use.
%  METHOD = 'GW' will use the traditional Golub-Welsch eigenvalue method,
%  which is best for when N is small. METHOD = 'FAST' will use the
%  Glaser-Liu-Rokhlin fast algorithm, which is much faster for large N.
%  By default LEGPTS uses 'GW' when N < 128.
%
%  See also chebpts, legpts, hermpts, and jacpts.

%  Copyright 2011 by The University of Oxford and The Chebfun Developers. 
%  See http://www.maths.ox.ac.uk/chebfun/ for Chebfun information.

%  'GW' by Nick Trefethen, March 2009 - algorithm adapted from [1].
%  'FAST' by Nick Hale, March 2010 - algorithm adapted from [2].
%
%  References:
%   [1] G. H. Golub and J. A. Welsch, "Calculation of Gauss quadrature
%       rules", Math. Comp. 23:221-230, 1969, 
%   [2] A. Glaser, X. Liu and V. Rokhlin, "A fast algorithm for the 
%       calculation of the roots of special functions", SIAM Journal  
%       on Scientific Computing", 29(4):1420-1438:, 2007.

method = 'default';
interval = [0 inf];

if n < 0
    error('CHEBFUN:lagpts:n','First input should be a positive number.');
end

% Return empty vector if n == 0
if n == 0
    x = []; w = []; v = []; return
end

% Check the inputs
if nargin > 1
    if nargin == 3
        interval = int; method = meth;
    elseif nargin == 2
        if ischar(int), method = int; else interval = int; end
    end
    if ~(strcmpi(method,'default') || strcmpi(method,'GW') || strcmpi(method,'fast'))
        error('CHEBFUN:lagpts:inputs',['Unrecognised input string.', method]); 
    end
    if isa(interval,'domain')
        interval = interval.endsandbreaks;
    end
    if numel(interval) > 2,
        warning('CHEBFUN:lagpts:domain',...
            'Piecewise intervals not supported and will be ignored.');
        interval = interval([1 end]);
    end
end

if ~sum(isinf(interval)) == 1
    error('CHEBFUN:lagpts:inf','lagpts only supports semi-infinite domains.');
end

% decide to use GW or FAST
if (n < 128 || strcmpi(method,'GW')) && ~strcmpi(method,'fast') % GW, see [1]
   alpha = 2*(1:n)-1;  beta = 1:n-1;     % 3-term recurrence coeffs
   T = diag(beta,1) + diag(alpha) + diag(beta,-1);  % Jacobi matrix
   [V,D] = eig(T);                       % eigenvalue decomposition
   [x,indx] = sort(diag(D));             % Laguerre points
   w = V(1,indx).^2;                     % Quadrature weights
   v = sqrt(x).*abs(V(1,indx)).';        % Barycentric weights
   v = v./max(v); v(2:2:n) = -v(2:2:n);
else                                                            % Fast, see [2]
   [x ders] = alg0_Lag(n);               % Nodes and L_n'(x)
   w = exp(-x)./(x.*ders.^2); w = w';    % Quadrature weights
   v = exp(-x/2)./ders;                  % Barycentric weights
   v = -v./max(abs(v));
end
w = (1/sum(w))*w;                        % Normalise so that sum(w) = 1

% Nonstandard interval
if ~all(interval == [0 inf])
    a = interval(1); b = interval(2);
    if isinf(b)
        x = x + a;
        w = w*exp(-a);
    else
        x = - x + b;
        w = w*exp(b);
    end
end
        

% -------------------- Routines for FAST algorithm ------------------------

function [x ders] = alg0_Lag(n)
ders = zeros(n,1);
xs = 1/(2*n+1);
n1 = 20;
n1 = min(n1, n);
x = zeros(n,1);
for k = 1:n1
    [xs ders(k)] = alg3_Lag(n,xs);
    x(k) = xs;  
    xs = 1.1*xs;
end
[x ders] = alg1_Lag(x,ders,n,n1);


% --------------------------- UNSCALED VERSION ------------------------------

function [roots ders] = alg1_Lag2(roots,ders,n,n1)
m = 30;
u = zeros(1,m+1); up = zeros(1,m+1);
for j = n1:n-1
    x = roots(j);
    h = rk2_Lag(pi/2,-pi/2,x,n) - x;

    r = x.*(n+.5 -.25*x);  p = x.^2;
    u(1:2) = [0; ders(j)]; 
    u(3) = (-x*u(2)/2-r*u(1))/p;
    u(4) = (-x*u(3)-(1+r)*u(2)/6-(n+.5-.5*x)*u(1))/p;
    up(1:3) = [u(2) ; 2*u(3) ; 3*u(4)];

    for k = 2:m-2
%         u(k+3)=(-x*(2*k+1)*u(k+2)-(k*k+r)*u(k+1)-k*(n+.5-.5*x)*u(k)+.25*k*(k-1)*u(k-1))/p;
        u(k+3)=(-x*(2*k+1)*(k+1)*u(k+2)-(k*k+r)*u(k+1)-(n+.5-.5*x)*u(k)+.25*u(k-1))/(p*(k+2)*(k+1));
        up(k+2) = (k+2)*u(k+3);
    end   
    
    hh = [1;cumprod(h+zeros(m,1))];
   
    for l = 1:5
%         hh = [1;cumprod(h*ones(m,1))];
        hh = [1;cumprod(h+zeros(m,1))];
        h = h - (u*hh)./(up*hh);
    end
    
    roots(j+1) = roots(j) + h;
    ders(j+1) = up*[1;cumprod(h*ones(m,1))];

end




% --------------------------- SCALED VERSION ------------------------------

function [roots ders] = alg1_Lag(roots,ders,n,n1)
m = 30;
% Storage
hh1 = ones(m+1,1); zz = zeros(m,1); u = zeros(1,m+1); up = zeros(1,m+1);
x = roots(n1);
for j = n1:n-1
    h = rk2_Lag(pi/2,-pi/2,x,n) - x;
    
    M = 1/h; M2 = M^2; M3 = M^3; M4 = M^4;

    r = x*(n + .5 - .25*x);  p = x^2;
    u(1:2) = [0; ders(j)/M]; 
    u(3) = -.5*u(2)/(M*x)-(n + .5 - .25*x)*u(1)/(x*M^2);
    u(4) = -u(3)/(M*x)+(-(1+r)*u(2)/6/M^2-(n+.5-.5*x)*u(1)/M^3)/p;
    up(1:3) = [u(2) ; 2*u(3)*M ; 3*u(4)*M];

    for k = 2:m-2
%         u(k+3) = (-x*(2*k+1)*u(k+2)-(k*k+r)*u(k+1)-k*(n+.5-.5*x)*u(k)+.25*k*(k-1)*u(k-1))/p;
        u(k+3) = (-x*(2*k+1)*(k+1)*u(k+2)/M-(k*k+r)*u(k+1)/M2-(n+.5-.5*x)*u(k)/M3+.25*u(k-1)/M4)/(p*(k+2)*(k+1));
        up(k+2) = (k+2)*u(k+3)*M;
    end    
    up(m+1) = 0;

    % Flip for more accuracy in inner product calculation.
    u = u(m+1:-1:1);  up = up(m+1:-1:1);

    % Newton iteration
    hh = hh1; hh(end) = M;    step = inf;  l = 0; 
    
    if M == 1
        Mhzz = (M*h)+zz;
        hh = [M;cumprod(Mhzz)];
        hh = hh(end:-1:1);
    end
    
    while (abs(step) > eps) && (l < 10)
        l = l + 1;
        step = (u*hh)/(up*hh);
        h = h - step;        
        Mhzz = (M*h)+zz;
        hh = [M;cumprod(Mhzz)];     % Powers of h (This is the fastest way!)
        hh = hh(end:-1:1);          % Flip for more accuracy in inner product 
    end
   
    % Update
    x = x + h;
    roots(j+1) = x;
    ders(j+1) = up*hh;    

end

% --------------------------- SCALED VERSION ------------------------------

function [roots ders] = alg4_Lag(roots,ders,n,n1)
m = 30;
% Storage
hh1 = ones(m+1,1); zz = zeros(m,1); u = zeros(1,m+1); up = zeros(1,m+1);
x = roots(n1);
for j = n1:n-1
    h = rk2_Lag(pi/2,-pi/2,x,n) - x;
    
    M = 1/h; M2 = M^2; M3 = M^3; M4 = M^4;

    r = x*(n + .5 - .25*x);  p = x^2;
    u(1:2) = [0; ders(j)/M]; 
    u(3) = -.5*u(2)/(M*x)-(n + .5 - .25*x)*u(1)/(x*M^2);
    u(4) = -u(3)/(M*x)+(-(1+r)*u(2)/6/M^2-(n+.5-.5*x)*u(1)/M^3)/p;
    up(1:3) = [u(2) ; 2*u(3)*M ; 3*u(4)*M];

    for k = 2:m-2
%         u(k+3) = (-x*(2*k+1)*u(k+2)-(k*k+r)*u(k+1)-k*(n+.5-.5*x)*u(k)+.25*k*(k-1)*u(k-1))/p;
        u(k+3) = (-x*(2*k+1)*(k+1)*u(k+2)/M-(k*k+r)*u(k+1)/M2-(n+.5-.5*x)*u(k)/M3+.25*u(k-1)/M4)/(p*(k+2)*(k+1));
        up(k+2) = (k+2)*u(k+3)*M;
    end    
    up(m+1) = 0;

    % Flip for more accuracy in inner product calculation.
    u = u(m+1:-1:1);  up = up(m+1:-1:1);

    % Newton iteration
    hh = hh1; hh(end) = M;    step = inf;  l = 0; 
    
    if M == 1
        Mhzz = (M*h)+zz;
        hh = [M;cumprod(Mhzz)];
        hh = hh(end:-1:1);
    end
    
    while (abs(step) > eps) && (l < 10)
        l = l + 1;
        step = (u*hh)/(up*hh);
        h = h - step;        
        Mhzz = (M*h)+zz;
        hh = [M;cumprod(Mhzz)];     % Powers of h (This is the fastest way!)
        hh = hh(end:-1:1);          % Flip for more accuracy in inner product 
    end
   
    % Update
    x = x + h;
    roots(j+1) = x;
    ders(j+1) = up*hh;    

end

% -------------------------------------------------------------------------

function [x1 d1] = alg3_Lag(n,xs)
[u up] = eval_Lag(xs,n);
theta = atan(sqrt(xs/(n+.5-.25*xs))*up/u);
x1 = rk2_Lag(theta,-pi/2,xs,n);

% Newton iteration
step = inf;  l = 0;    
while (abs(step) > eps || abs(u) > eps) && (l < 200)
    l = l + 1;
    [u up] = eval_Lag(x1,n);
    step = u/up;
    x1 = x1 - step;
end

[ignored d1] = eval_Lag(x1,n);

% -------------------------------------------------------------------------

function [L Lp] = eval_Lag(x,n)
Lm2 = 0; Lm1 = exp(-x/2); Lpm2 = 0; Lpm1 = 0;
for k = 0:n-1
    L = ((2*k+1-x).*Lm1-k*Lm2)/(k+1);
    Lp = ((2*k+1-x).*Lpm1-Lm1-k*Lpm2)/(k+1);
    Lm2 = Lm1; Lm1 = L; Lpm2 = Lpm1; Lpm1 = Lp;
end

% -------------------------------------------------------------------------

function x = rk2_Lag(t,tn,x,n)
m = 10; h = (tn-t)/m;
for j = 1:m
    f1 = (n+.5-.25*x);
    k1 = -h/(sqrt(f1/x)+.25*(1/x-.25/f1)*sin(2*t));
    t = t+h;  x = x+k1;   f1 = (n+.5-.25*x);
    k2 = -h/(sqrt(f1/x)+.25*(1/x-.25/f1)*sin(2*t));
    x = x+.5*(k2-k1);
end
